package cn.stimd.spring.transaction.support;

import cn.stimd.spring.transaction.PlatformTransactionManager;
import cn.stimd.spring.transaction.TransactionDefinition;
import cn.stimd.spring.transaction.TransactionException;
import cn.stimd.spring.transaction.TransactionStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.List;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager {
    private Log logger = LogFactory.getLog(getClass());
    public static final int SYNCHRONIZATION_ALWAYS = 0;
    public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
    public static final int SYNCHRONIZATION_NEVER = 2;

    //默认总是需要同步的
    private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
    private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;


    @Override
    public TransactionStatus getTransaction(TransactionDefinition definition) {
        //TODO 涉及事务的传播，先简化处理，后续完善
        Object transaction = doGetTransaction();
        doBegin(transaction, definition);
        logger.info("[Tx] [事务管理] --> 事务不存在，开启新事务");
        DefaultTransactionStatus status = new DefaultTransactionStatus(transaction, null, true, true, definition.isReadOnly());
        prepareSynchronization(status, definition);
        return status;
    }


    @Override
    public void commit(TransactionStatus status) {
        if (status.isCompleted()) {
            throw new IllegalStateException("提交失败，事务已提交或回滚");
        }

        //1. 如果rollbackOnly被标记为true，说明内层事务发生异常，需要回滚
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        if(defStatus.isGlobalRollbackOnly()){
            processRollback(defStatus);

            //如果是新事务，即最外层事务，需要抛出异常，宣告整个事务执行失败
            if(status.isNewTransaction()){
                throw new TransactionException("事务被标记为rollback-only，发生回滚");
            }
            return;
        }

        //2. 执行提交流程
        processCommit(defStatus);
    }


    private void processCommit(DefaultTransactionStatus status) {
        try {
            boolean beforeCompletionInvoked = false;
            try {
                triggerBeforeCommit(status);
                triggerBeforeCompletion(status);
                beforeCompletionInvoked = true;

                //只有新事务，才执行提交操作
                if(status.isNewTransaction()){
                    doCommit(status);
                    logger.info("[Tx] [事务管理] --> 提交事务");
                }
            } catch (RuntimeException | Error ex) {
                if (!beforeCompletionInvoked) {
                    triggerBeforeCompletion(status);
                }
                doRollbackOnCommitException(status, ex);
                throw ex;
            }

            try {
                triggerAfterCommit(status);
            } finally {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
            }
        } finally {
            //清理操作
            cleanupAfterCompletion(status);
        }
    }


    //回滚在提交时发生的异常
    private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) throws TransactionException {
        try{
            //新事务，执行回滚操作
            if (status.isNewTransaction()) {
                doRollback(status);
            }
            //内层事务，将rollbackOnly标记设置为true
            else if (status.hasTransaction()) {
                doSetRollbackOnly(status);
            }
        } catch (RuntimeException|Error e){
            logger.error("Commit exception overridden by rollback exception", ex);
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw e;
        }
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    }


    @Override
    public void rollback(TransactionStatus status) {
        if (status.isCompleted()) {
            throw new IllegalStateException("回滚失败，事务已提交或回滚");
        }
        processRollback((DefaultTransactionStatus) status);
    }


    private void processRollback(DefaultTransactionStatus status) {
        try {
            try {
                triggerBeforeCompletion(status);

                //1. 只有新事务，才执行真正的回滚操作
                if (status.isNewTransaction()) {
                    doRollback(status);
                    logger.info("[Tx] [事务管理] --> 回滚事务");
                }
                //2. 内层事务，将rollbackOnly标记设置为true
                else if(status.hasTransaction()){
                    doSetRollbackOnly(status);
                }
                else{
                    logger.debug("Should roll back transaction but cannot - no transaction available");
                }
            } catch (RuntimeException | Error e) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw e;
            }
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
        } finally {
            //清理操作
            cleanupAfterCompletion(status);
        }
    }


    //恢复事务
    private void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) {
        //TODO 待完善
    }


    protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
                    definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ? definition.getIsolationLevel() : null);
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
            TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
            TransactionSynchronizationManager.initSynchronization();
        }
    }

    // ------------------------------------------------------------- TransactionSynchronization回调的触发


    protected void triggerBeforeCommit(DefaultTransactionStatus status) {
        if (status.isNewSynchronization()) {
            TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
        }
    }

    private void triggerBeforeCompletion(DefaultTransactionStatus status) {
        if (status.isNewSynchronization()) {
            TransactionSynchronizationUtils.triggerBeforeCompletion();
        }
    }

    private void triggerAfterCommit(DefaultTransactionStatus status) {
        if (status.isNewSynchronization()) {
            TransactionSynchronizationUtils.triggerAfterCommit();
        }
    }

    private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
        if (status.isNewSynchronization()) {
            List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
            TransactionSynchronizationManager.clearSynchronization();
            if (!status.hasTransaction() || status.isNewTransaction()) {
                TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus);
            }
        }
    }

    //清理操作，提交和回滚均会触发
    private void cleanupAfterCompletion(DefaultTransactionStatus status) {
        //将completed属性设置为true
        status.setCompleted();
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }

        if (status.isNewTransaction()) {
            doCleanupAfterCompletion(status.getTransaction());
        }

        if (status.getSuspendedResources() != null) {
            resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
        }
    }


    private boolean isNewSynchronization(){
        return this.transactionSynchronization != SYNCHRONIZATION_NEVER;
    }


    /**
     * 允许子类自定义事务对象，父类并不关心内部结构，回调时子类强转回原类型处理
     */
    protected abstract Object doGetTransaction();
    protected abstract boolean isExistingTransaction(Object transaction);
    protected abstract void doBegin(Object transaction, TransactionDefinition definition);
    protected abstract Object doSuspend(Object transaction);
    protected abstract void doResume(Object transaction, Object suspendResources);
    protected abstract void doCommit(DefaultTransactionStatus status);
    protected abstract void doRollback(DefaultTransactionStatus status);
    protected abstract void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException;
    protected abstract void doCleanupAfterCompletion(Object transaction);


    protected static class SuspendedResourcesHolder { }
}
